package com.juqimiao.outposts.spring.oauth2.api.configuration;


import com.juqimiao.outposts.spring.oauth2.mobile.SecurityCodeGenerator;
import com.juqimiao.outposts.spring.oauth2.mobile.code.MobileCodeGenerator;
import com.juqimiao.outposts.spring.oauth2.mobile.provider.SecurityCodeAuthenticationProvder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.password.PasswordEncoder;

import javax.annotation.Resource;


/**
 * WebSecurityAdaptConfig.
 */
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true,
        securedEnabled = true,
        jsr250Enabled = true)
public class WebSecurityAdaptConfig extends WebSecurityConfigurerAdapter {


    @Resource
    private PasswordEncoder passwordEncoder;

    @Resource(name = "OAuthUserDetailsServiceImpl")
    private UserDetailsService userDetailsService;

    @Autowired
    private SecurityCodeGenerator securityCodeGenerator;

    @Bean
    public SecurityCodeGenerator getSecurityCodeGenerator(){
        return new MobileCodeGenerator();
    }
    /**
     * spring security设置
     * prePostEnabled = true,
     * securedEnabled = true,
     * jsr250Enabled = true
     * 开启三种可以在方法上面加权限控制的注解.
     *
     * @param http http
     * @throws Exception exception
     */
    @Override
    public void configure(final HttpSecurity http) throws Exception {
        //需要正常运行的话，需要取消这段注释，原因见下面小节
        http
                //定义哪些url需要被保护  哪些不需要保护
                .authorizeRequests()
                //定义这两个链接不需要登录可访问
                .antMatchers("/oauth/authorize",
                        "/oauth/token",
                        "/oauth/confirm_access",
                        "/oauth/error",
                        "/oauth/check_token",
                        "/oauth/token_key")
                .permitAll()
                //定义所有的都不需要登录  目前是测试需要
                .antMatchers("/**").permitAll()
                //其他的都需要登录
                .anyRequest().authenticated()
                .and()
                //防止跨站请求  spring security中默认开启
                .csrf()
                .disable();
    }

    /**
     * 配置自定义用户详情服务。
     *
     * @return 返回 {@link UserDetailsService}.
     */
    @Override
    @Bean
    protected UserDetailsService userDetailsService() {
        return userDetailsService;
    }

    /**
     * 需要配置这个支持password模式.
     * support password grant type.
     *
     * @return AuthenticationManager.
     * @throws Exception Exception.
     */
    @Override
    @Bean
    public AuthenticationManager authenticationManagerBean() throws Exception {
        //这个是比client credentials模式新增的配置，主要配置用户以及authenticationManager
        //这里记得注入authenticationManager来支持password模式
        return authenticationManager();
    }


    /**
     * configure.
     *
     * @param auth AuthenticationManagerBuilder.
     * @throws Exception Exception.
     */
    @Override
    protected void configure(final AuthenticationManagerBuilder auth)
            throws Exception {
        SecurityCodeAuthenticationProvder securityCodeAuthenticationProvder = new SecurityCodeAuthenticationProvder();
        securityCodeAuthenticationProvder.setUserDetailsService(userDetailsService);
        securityCodeAuthenticationProvder.setCodeGenerator(securityCodeGenerator);
        auth.authenticationProvider(securityCodeAuthenticationProvder);
        //UserDetailsService在这里被用于验证用户的有效性。
        // PasswordEncoder为了确保传入的密码使用一致的加密方式被加密然后做比较。
        auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder);

    }
}
